﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.IO.Ports;
using System.Runtime.Serialization;
using System.Text;
using Pahan.McuFusion.Common;
using Pahan.McuFusion.Common.ComponentModel;

namespace Pahan.McuFusion.FusionBoot
{
    [Serializable]
    internal class FusionBootSerialInterface : IFusionBootInterface, ISerializable
    {
        #region Constructors

        internal FusionBootSerialInterface(FusionBootSerialInterfaceInfo info)
        {
            this.Info = info;
            port = new SerialPort();
            port.ReadTimeout = defaultReadTimeout;
            port.WriteTimeout = defaultWriteTimeout;
        }

        protected FusionBootSerialInterface(SerializationInfo info, StreamingContext context)
        {
            port = new SerialPort(info.GetString(portNameKey));
            ReadTimeout = info.GetInt32(readTimeoutKey);
            WriteTimeout = info.GetInt32(writeTimeoutKey);
            Info = (FusionBootSerialInterfaceInfo)info.GetValue(infoKey,
                typeof(FusionBootSerialInterfaceInfo));
        }

        #endregion

        #region Implementation of IDisposable

        /// <summary>
        /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
        /// </summary>
        /// <filterpriority>2</filterpriority>
        public void Dispose()
        {
            port.Dispose();
        }

        #endregion

        #region Implementation of IHardwareInterface

        /// <summary>
        /// Gets the information about the hardware interface.
        /// </summary>
        public HardwareInterfaceInfo Info
        {
            get;
            private set;
        }

        /// <summary>
        /// Opens the hardware interface.
        /// </summary>
        public void Open()
        {
            port.Open();
        }

        /// <summary>
        /// Closes the hardware interface.
        /// </summary>
        public void Close()
        {
            port.Close();
        }

        #endregion

        #region Implementation of IFusionBootInterface

        public void Write(IList<byte> data)
        {
            byte[] bytes = new byte[data.Count];
            data.CopyTo(bytes, 0);
            port.Write(bytes, 0, bytes.Length);
        }

        public void WriteByte(byte data)
        {
            byte[] bytes = new[] {data};
            port.Write(bytes, 0, 1);
        }

        public byte ReadByte()
        {
            int result = port.ReadByte();
            if (result == -1)
            {
                throw new InvalidOperationException("The serial port stream has reached the end.");
            }
            return (byte)result;
        }

        public void DiscardInputBuffer()
        {
            port.DiscardInBuffer();
        }

        public void DiscardOutputBuffer()
        {
            port.DiscardOutBuffer();
        }

        #endregion

        #region Public Members

        [TypeConverter(typeof(SerialPortNameTypeConverter))]
        [Description("The name of a physical or virtual COM port to use.")]
        public String Port
        {
            get
            {
                return port.PortName;
            }
            set
            {
                port.PortName = value;
            }
        }

        [DefaultValue(defaultReadTimeout)]
        [DisplayName("Read Time-out, ms")]
        [Description("The number of milliseconds before a time-out occurs when a read operation does not finish.")]
        public int ReadTimeout
        {
            get
            {
                return port.ReadTimeout;
            }
            set
            {
                port.ReadTimeout = value;
            }
        }

        [DefaultValue(defaultWriteTimeout)]
        [DisplayName("Write Time-out, ms")]
        [Description("The number of milliseconds before a time-out occurs when a write operation does not finish.")]
        public int WriteTimeout
        {
            get
            {
                return port.WriteTimeout;
            }
            set
            {
                port.WriteTimeout = value;
            }
        }

        #endregion

        #region Private Members

        [NonSerialized]
        private SerialPort port;

        private const int defaultReadTimeout = 5000;
        private const int defaultWriteTimeout = 5000;

        private const String infoKey = "Info";
        private const String portNameKey = "PortName";
        private const String readTimeoutKey = "ReadTimeout";
        private const String writeTimeoutKey = "WriteTimeout";

        #endregion

        #region Implementation of ISerializable

        /// <summary>
        /// Populates a <see cref="T:System.Runtime.Serialization.SerializationInfo"></see> with the data needed to serialize the target object.
        /// </summary>
        /// <param name="context">The destination (see <see cref="T:System.Runtime.Serialization.StreamingContext"></see>) for this serialization. </param>
        /// <param name="info">The <see cref="T:System.Runtime.Serialization.SerializationInfo"></see> to populate with data. </param>
        /// <exception cref="T:System.Security.SecurityException">The caller does not have the required permission. </exception>
        public void GetObjectData(SerializationInfo info, StreamingContext context)
        {
            info.AddValue(infoKey, Info);
            info.AddValue(portNameKey, port.PortName);
            info.AddValue(readTimeoutKey, port.ReadTimeout);
            info.AddValue(writeTimeoutKey, port.WriteTimeout);
        }

        #endregion
    }
}
